// 所有项目 所有端（app端除外） 【上传图片组件】
// 供所有项目直接使用
// 依赖：WMUtils/beastUtils、
//      libSrc/constants/api、libUtils/beastBase、
//      pageCommon/constants/COMMON、
//      styles/components/uploadBase.less

import React, { Component } from 'react'
import { findDOMNode } from 'react-dom'
//import {alertMsg} from 'beastBase'
import { message, Button } from 'antd';
import {isObjectValEqual} from '/common/utils/utils'
import { compress, getBase64FormData } from './compressUtils'

import merge from 'lodash/merge'
import forEach from 'lodash/forEach'
import map from 'lodash/map'
import toLower from 'lodash/toLower'
import isArray from 'lodash/isArray'
import isObject from 'lodash/isObject'
import isString from 'lodash/isString'
import isFunction from 'lodash/isFunction'
import isEmpty from 'lodash/isEmpty'

import './uploadBase.less'

const API_DOMAIN = '',UPLOAD_IMG = '',UPLOAD_VIDEO = '';

/**
 * 将 B 转换成 K 或者 M
 * @param size
 * @private
 */
function byte2KOM(size) {
    let reStr;

    if (1024 * 1024 <= size) { // M
        reStr = `${(size / (1024 * 1024)).toFixed(2)}M`;
    } else if (1024 <= size) { // K
        reStr = `${(size / (1024)).toFixed(2)}K`;
    } else {
        reStr = `${size}B`
    }

    return reStr;
}

const fileTypesO = {
    pic_types: {
        t_name: '图片',
        t_arr: ['jpg', 'png', 'jpeg', 'gif'],
        t_accept: 'image/jpg,image/png,image/jpeg,image/gif'
    },
    video_types: {
        t_name: '视频',
        t_arr: [],
        //t_arr: ['3gp', '3gpp', 'mp4', 'mpeg', 'mpg', 'ogg', 'mov'],
        t_accept: 'video/3gpp,audio/mp4,video/mp4,video/mpeg,video/mpeg,application/ogg,audio/ogg,.mov,.avi,.flv'
        //t_accept: '.avi,.divx,.3gp,.mov,.mpeg,.mpg,.xvid,.flv,.asf,.rm,.dat,.mp4,.vob,.mkv,.wmv,.rmvb'
    },
    file_types: {
        t_name: '文件',
        t_arr: [],
        t_accept: ''
    }
};

export default class UploadBase extends Component {

    constructor(props) {
        super(props);
        this.ajaxXhrObj = {}; // 每次 ajax 请求时候的 xhr 对象，可用于中断请求等
        //    用于压缩图片的 canvas
        this.canvas = null;
        this.ctx = null;
        //    瓦片 canvas
        this.tCanvas = null;
        this.tctx = null;

        this.state = {
            isFetching: false
        };
    }

    componentDidMount() {
        //    用于压缩图片的 canvas
        this.canvas = document.createElement("canvas");
        this.ctx = this.canvas.getContext('2d');
        //    瓦片 canvas
        this.tCanvas = document.createElement("canvas");
        this.tctx = this.tCanvas.getContext("2d");
    }

    shouldComponentUpdate(nextProps, nextState) {
        if (!isObjectValEqual(this.props, nextProps)) {
            return true;
        }
        if (!isObjectValEqual(this.state, nextState)) {
            return true;
        }
        return false;
    }

    componentWillUnmount() {
        forEach(this.ajaxXhrObj, (item, key) => {
            if (isObject(item) && isObject(item['xhrO'])) { // 将所有未完成的上传终止
                item['xhrO'].abort();
            }
        });
    }

    /**
     * 图片压缩上传
     * @param file
     * @private
     */
    _uploadBase64Img(file) {
        this.setState({isFetching: true, compUpdateTime: Math.random() * 1000}, () => {
            const {
                compressParamsImportant,
                compressRatio,
                } = this.props;
            let $this = this,
                reader = new FileReader(),
                canvas = this.canvas, ctx = this.ctx,
                tCanvas = this.tCanvas, tctx = this.tctx;

            reader.onload = () => {
                var result = reader.result;
                var img = new Image();
                img.src = result;

//      图片加载完毕之后进行压缩，然后上传
                if (img.complete) {
                    callback();
                } else {
                    img.onload = callback;
                }

                function callback() {
                    let cRatio = compressRatio;

                    if (!compressParamsImportant) { // 不一定以传入的压缩参数为准

                        if (file.size >= 7 * 1024 * 1024 && cRatio > 0.1) {
                            // >=5M，压缩 0.1
                            cRatio = 0.1;

                        } else if (file.size >= 2 * 1024 * 1024 && cRatio > 0.2) {
                            // 如果图片大于 3M，压缩率 0.2
                            cRatio = 0.2;
                        } else if (file.size >= 500 * 1024 && cRatio > 0.3) {
                            // 如果图片大于 500K，压缩率 0.3
                            cRatio = 0.3;
                        }
                    }

                    let data = compress(img, canvas, ctx, tCanvas, tctx, cRatio);
                    let tmpData = getBase64FormData(data, file);
                    $this.uploadFunc(tmpData, file);
                    img = null;
                }

            };

            reader.readAsDataURL(file);
        });
    }

    /**
     * 上传文件
     * @param file
     * @private
     */
    _uploadFile(file) {
        let tmpData = new FormData();
        tmpData.append(file.ind, file); // 添加文件
        this.uploadFunc(tmpData, file);
        //console.log('this.ajaxXhr', this.ajaxXhr)
    }

    /**
     * 上传
     * @param tmpData  FormData
     * @param file 文件
     * @private
     */
    uploadFunc(tmpData, file) {
        const $this = this;
        let uploadUrl = (() => {
            const {uploadType} = this.props.options;

            switch (uploadType) {
                case 'pic':
                    return UPLOAD_IMG;
                case 'video':
                    return UPLOAD_VIDEO;
            }
            return '';

        })();

        if ('' != $.trim(this.props.options.uploadUrl)) {
            uploadUrl = this.props.options.uploadUrl;
        }

        if ('' == uploadUrl) {
            console.error('upload component have no uploadUrl!');
            return false;
        }
        this.setState({isFetching: true, percent: ''});

        $this.ajaxXhrObj[`xhr_${file.ind}`] = {};
        $this.ajaxXhrObj[`xhr_${file.ind}`]['percent'] = ''; // 上传进度
        $this.ajaxXhrObj[`xhr_${file.ind}`]['file'] = file;
        $this.ajaxXhrObj[`xhr_${file.ind}`]['xhrO'] = $.ajax({
            url: uploadUrl,
            type: 'POST',
            xhr: function () {
                //var xhr = new XMLHttpRequest();
                //xhr.upload.onprogress = function (e) {
                //} // 参考： Hacker丶wand 链接：http://www.imooc.com/article/5879

                //通过$.ajaxSettings.xhr();获得XMLHttpRequest对象
                var xhr = $.ajaxSettings.xhr();

                if (xhr.upload) {
                    xhr.upload.onload = function () {
                        //alert('finish downloading')
                    };
                    //如果xhr对象支持绑定时就把监听函数绑定上去
                    xhr.upload.onprogress = function (e) {

                        if (e.lengthComputable) {
                            var percent = Math.floor(e.loaded * 100 / e.total);//计算百分比
                            //console.log('e:', e);
                            $this.ajaxXhrObj[`xhr_${file.ind}`]['percent'] = `${percent}%`;
                            $this.ajaxXhrObj[`xhr_${file.ind}`]['loadedMsg'] =
                                `${byte2KOM(e.loaded)} / ${byte2KOM(e.total)}`;
                            $this.setState({
                                updateCompTime: Math.ceil(Math.random() * 1000)
                            });
                        }
                    };
                }
                return xhr;
            },
            data: tmpData,
            cache: false,
            dataType: 'json',
            processData: false, // (默认: true) 默认情况下，发送的数据将被转换为对象(技术上讲并非字符串) 以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息，请设置为 false。
            contentType: false, // (默认: "application/x-www-form-urlencoded") 发送信息至服务器时内容编码类型。默认值适合大多数应用场合
            beforeSend: function () {
                //传输之前的处理
            },
            success: function (rsp, textStatus, jqXHR) {
                //console.log(rsp.data.pic);

                if ('production' != process.env.NODE_ENV) {
                    console.info(`success 从 ajaxXhrObj 中移除xhr_${file.ind}`);
                }
                delete $this.ajaxXhrObj[`xhr_${file.ind}`];

                if ('production' != process.env.NODE_ENV) {
                    console.info(`success $this.ajaxXhrObj:`, $this.ajaxXhrObj);
                }

                if (rsp.errcode >= 90000 && rsp.url && rsp.url.length > 0) {
                    message.warning(rsp.errmsg);
                    window.location.href = rsp.url;

                } else if (rsp.errcode != 0) {
                    message.warning(rsp.errmsg);
                    // 执行上传失败的回调方法
                    $this.props.uploadFailCb && (
                        $this.props.uploadFailCb(rsp)
                    );
                    window.setTimeout(function () {
                        $this.setState({
                            isFetching: false,
                            updateCompTime: Math.ceil(Math.random() * 1000)
                        });

                    }, 1000);
                } else {

                    if (isFunction($this.props.uploadSucCb)) {
                        $this.props.uploadSucCb(rsp);
                    }

                    window.setTimeout(function () {

                        $this.setState({
                            isFetching: false,
                            updateCompTime: Math.ceil(Math.random() * 1000)
                        });

                    }, 1000);
                }
                // 清空 input[file] 的值
                $(findDOMNode($this.refs.upRef)).val('');
            },
            error: function (jqXHR, textStatus, errorThrown) {
                // 清空 input[file] 的值
                $(findDOMNode($this.refs.upRef)).val('');
                // Handle errors here
                // 中止请求
                if ('abort' == textStatus) {
                    message.info('已取消本次上传');
                    $this.setState({isFetching: false, percent: ''}); // 清空进度条
                } else {
                    $this.setState({isFetching: false});
                    console.log('textStatus: ' + textStatus);
                    console.log('errorThrown: ' + errorThrown);
                    message.error('操作失败', '当前网络不稳定，请稍后再试');
                }

                window.setTimeout(function () {

                    if ('production' != process.env.NODE_ENV) {
                        console.log(`error 从 ajaxXhrObj 中移除xhr_${file.ind}`);
                    }
                    delete $this.ajaxXhrObj[`xhr_${file.ind}`];
                    $this.setState({
                        percent: '',
                        updateCompTime: Math.ceil(Math.random() * 1000)
                    });

                    if ('production' != process.env.NODE_ENV) {
                        console.log(`error $this.ajaxXhrObj:`, $this.ajaxXhrObj);
                    }
                }, 300);

                // 执行上传失败的回调方法
                $this.props.uploadErrCb && (
                    $this.props.uploadErrCb(jqXHR, textStatus, errorThrown)
                );
            }
        });
    }

    _handleClickAbort(e) {
        e.preventDefault();
        const ind = $(e.currentTarget).attr('data-ind');

        if (isObject(this.ajaxXhrObj[`xhr_${ind}`])
            && isObject(this.ajaxXhrObj[`xhr_${ind}`]['xhrO'])) {
            this.ajaxXhrObj[`xhr_${ind}`]['xhrO'].abort();
            console.log('_handleClickAbort this.ajaxXhrObj', this.ajaxXhrObj);
        }
    }

    /**
     * 校验是否可以上传
     * @param file
     * @param key
     */
    isFileCanUpload(file, key) {
        const {
            uploadType, /*上传的文件格式（'pic':图片; 'file':文件;），默认是图片*/
            fileTypes, // 支持的文件类型，例如：['jpg','png','jpeg']、['mp4']
            fileMaxSize
            } = this.props.options;
        const {
            needCompress,
            compressSize
            } = this.props;
        let fileTypesArr = (isArray(fileTypes) && fileTypes.length > 0) ?
            fileTypes : fileTypesO[`${uploadType}_types`]['t_arr'];
        let checkFileType = (isArray(fileTypesArr) && fileTypesArr.length > 0),
            cnTip = fileTypesO[`${uploadType}_types`]['t_name'];

        if (checkFileType &&
            -1 === fileTypesArr.indexOf(toLower(file.type.split('/')[1]))) {
            message.error(
                `暂不支持“${file.name}”的${cnTip}类型`,
                `支持的${cnTip}类型：${fileTypesArr.join('、')}`
            );
        } else {

            if (file.size >= fileMaxSize) {
                message.error(`您的“${file.name}”${cnTip}大小过大，${cnTip}大小不可大于 ${byte2KOM(fileMaxSize)}`);
            } else {
                file.ind = key;
                //== 上传
                // 如果是上传图片且标示了需要压缩且大于compressSize或500k，则压缩之后上传
                if ('pic' == uploadType && needCompress && (file.size >= compressSize || file.size >= 500 * 1024)) {
                    this._uploadBase64Img(file);
                } else {
                    this._uploadFile(file);
                }
            }
        }
    }

    /**
     * 是否所有文件都上传完成了，即当前没有文件正在上传了
     */
    isAllUploaded() {
        return isEmpty(this.ajaxXhrObj);
    }

    // 当 input[type=file] 的值改变，即选择了图片之后
    _handleImageChange(e) {
        e.preventDefault();
        //console.log('e.target.files', e.target.files);
        const {
                onceNum, // 单次选择的文件个数，默认单次选择 5 个
                disabled // 不可操作
                } = this.props.options,
            files = e.target.files;

        if (disabled) {
            return false;
        }

        if (files.length > 0) {

            // 校验文件数量
            if (files.length > onceNum) {
                message.error(`选择的文件个数过多`,
                    `本次上传最多可选择 ${onceNum} 个文件`);
                return false;
            }
            // 校验文件类型
            forEach(files, (file, key) => {
                this.isFileCanUpload(file, key)
            });

        }

        //let reader = new FileReader();
        //let file = e.target.files[0];
        //let files = e.target.files;
        //
        //reader.onloadend = () => {
        //    console.log(reader.result)
        //    let tO = merge({}, this.state);
        //    this.setState({
        //        file: file,
        //        imagePreviewUrl: reader.result
        //    });
        //}
        //
        //reader.readAsDataURL(files[0])
    }

    render() {
        //const {disabled, uploadType} = this.props.options;

        return (
            <div className={`up-base-cn ps-r ${this.props.exClassName}`} ref="upRef">

                {this._renderUploadBtn()}
                <div className="hp-contain">
                    {
                        //this.state.percent !== '' &&

                        map(this.ajaxXhrObj, (obj, key) => {

                            if (isObject(obj) && isObject(obj.file)) {
                                let oItem = obj.file;

                                return (
                                    <div className="help-block" key={key}>
                                        <span className="up-file-name">{oItem.name}</span>
                                        <span
                                            className="up-file-size">{`（${obj.loadedMsg ? obj.loadedMsg : byte2KOM(oItem.size)}）`}</span>
                                        {
                                            '100%' == obj.percent ? (
                                                <span className="abort-btn">上传完成</span>
                                            ) : (
                                                <a className="abort-btn" role="button"
                                                   data-ind={oItem.ind}
                                                   onClick={this._handleClickAbort.bind(this)}>
                                                    取消上传
                                                </a>
                                            )
                                        }
                                        <small
                                            className="help-percent text-green f12 ">{obj.percent ? obj.percent : ''}</small>
                                        <div className="progress xs">
                                            <div className="progress-bar progress-bar-green"
                                                 style={{width: obj.percent}}></div>
                                        </div>
                                    </div>
                                )
                            } else {
                                return null;
                            }

                        })
                    }
                </div>

            </div>
        )
    }

    _renderUploadBtn() {
        const {isMulti, onceNum, txt, txtTip, disabled, uploadType} = this.props.options;
        const isFetching = this.state.isFetching;
        let tipTxt = (!isEmpty(this.ajaxXhrObj) || isFetching) ? '正在上传...' : txt;

        if (disabled || isFetching || 0 >= onceNum || !isEmpty(this.ajaxXhrObj)) {
            return (
                <a href="#" className="btn btn-default my-upload-btn" disabled="true">
                    <i className="fa fa-upload"/> {tipTxt}
                    <span className="text-danger">{txtTip}</span>
                </a>
            )
        }

        let acceptTypes = fileTypesO[`${uploadType}_types`]['t_accept'];

        return (
            <a href="#" className="btn btn-default my-upload-btn">
                <i className="fa fa-upload"/> {tipTxt}
                <span className="text-danger">{txtTip}</span>
                {
                    isMulti ?
                        (<input type="file" className="input_file"
                                accept={acceptTypes}
                                onChange={(e) => this._handleImageChange(e)}
                                name="file" multiple/>)
                        :
                        (<input type="file" className="input_file"
                                accept={acceptTypes}
                                onChange={(e) => this._handleImageChange(e)}
                                name="file"/>)
                }

            </a>
        )
    }

}

UploadBase.defaultProps = {
    exClassName: '',
    options: {
        txt: '', // 按钮的文字，例如：点击上传图片
        txtTip: '', // 按钮上的提示文字，例如：（限制5张）
        uploadType: 'pic', /*上传的文件格式（'pic':图片; 'file':文件;），默认是图片*/
        fileTypes: ['jpg', 'png', 'jpeg'], // 支持的文件类型，例如：['jpg','png','jpeg']、['mp4']
        isMulti: true, // 是否可上传多个，默认可以
        onceNum: 5, // 单次选择的文件个数，默认单次选择 5 个
        uploadUrl: '', // 上传图片的接口路径（api）
        disabled: false // 不可操作
    },
    needCompress: true, // 是否需要压缩
    compressSize: 200 * 1024, // 图片大小 >=compressSize 时会执行压缩
    compressRatio: 0.5, // 图片压缩率
    compressParamsImportant: false, // 是否必须以传入的压缩参数为准
    uploadSucCb: undefined, // 上传文件成功之后的回调函数，参数：(rsp)
    uploadFailCb: undefined, // 上传文件失败的回调函数，参数：(jqXHR, textStatus, errorThrown)
    uploadErrCb: undefined, // 上传文件错误的回调函数，参数：(jqXHR, textStatus, errorThrown)
};